package za.org.jcicada.genss.sensor.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import za.org.jcicada.genss.sensor.ISensorType;

/**
 * Discrete sensor type is a set of strings. This was previously 
 * class TypeDiscrete<E extends Enum<E>> but this would have coupled observers
 * of such sensors too tightly to their implementation.
 * @author rcrida
 *
 */
public class TypeDiscrete implements ISensorType {
	private static final long serialVersionUID = 1L;
	
    private String[] valueArray;
    
    private Map<String, Integer> valueIndex = new HashMap<String, Integer>();
    
    /**
     * Factory method used to access instance of TypeDiscrete
     * @param values set of legal sensor values for this type
     * @return an instance of TypeDiscrete
     */
    static public TypeDiscrete getInstance(Set<String> values) {
        return new TypeDiscrete(values);
    }
    
    /**
     * Factory method used to access instance of TypeDiscrete
     * @param values set of legal sensor values for this type
     * @return an instance of TypeDiscrete
     */
    static public TypeDiscrete getInstance(String[] values) {
        return new TypeDiscrete(values);
    }

    /**
     * Factory method used to access instance of TypeDiscrete
     * @param clazz should be an enum class
     * @return an instance of TypeDiscrete
     */
    static public TypeDiscrete getInstance(Class<?> clazz) {
    	String[] array = new String[clazz.getEnumConstants().length];
        for (Object state : clazz.getEnumConstants()) {
            array[((Enum<?>)state).ordinal()] = state.toString();
        }
        return new TypeDiscrete(array);
    }
    
    /**
     * Private constructor forces use of getInstance().
     * @param values set of legal sensor values for this type
     */
    private TypeDiscrete(Set<String> values) {
    	this(values.toArray(new String[values.size()]));
    }
    
    /**
     * Private constructor forces use of getInstance().
     * @param values array of legal sensor values for this type
     */
    private TypeDiscrete(String[] values) {
        //this.valueSet = new HashSet<String>(Arrays.asList(values));
        this.valueArray = values.clone();
        int index = 0;
        for (String value : values) {
        	valueIndex.put(value, index);
        	index++;
        }
    }
    
    /**
     * Check that the specified value is contained within the legal set of strings.
     */
    public boolean isValid(Object value) {
        return valueIndex.containsKey(value);
    }
    
    public Set<String> getValueSet() {
    	return new HashSet<String>(valueIndex.keySet());
    }
    
    @Override
    public String toString() {
    	return "Discrete";
    }
    
    @Override
    public boolean equals(Object aOther) {
        if (this == aOther) return true;
        if (!(aOther instanceof TypeDiscrete)) return false;
        TypeDiscrete other = (TypeDiscrete)aOther;
        return valueIndex.equals(other.valueIndex);
    }
    
    @Override
    public int hashCode() {
        return valueIndex.keySet().hashCode();
    }

    public String[] toStringArray() {
    	return valueArray.clone();
    }

    public Object valueOf(String string) {
        if (!isValid(string)) {
            throw new IllegalArgumentException(String.format("%s not in %s", string, valueArray));
        }
        return string;
    }
    
    public double doubleValueOf(Object value) {
    	Integer ordinal = valueIndex.get(value);
    	if (ordinal != null) {
    		return ordinal.doubleValue();
    	}
    	else {
    		throw new IllegalArgumentException();
    	}
    }

}
